from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QTimer
from serial import Serial
from serial.tools import list_ports
from threading import Thread
from concurrent.futures import ThreadPoolExecutor
from concurrent.futures import Future
import time

def now():
    return time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())

 
# 连接串口 , 
def __connect__(port:str, baudrate:int):
    print("[%s]:开始连接" % now())
    s = Serial(port=port, baudrate=baudrate, timeout=1200)
    print("[%s]:连接结束" % now())
    return s

class SerialObject(QObject):
    # 发生错误信号
    error = pyqtSignal(Exception)
    # 连接状态变化的信号
    # 入参表示当前串口的连接状态, True: 已连接, False: 未连接
    statusChanged = pyqtSignal(bool)
    # 接收到串口传递的字节信息
    received = pyqtSignal(bytes)

    def __init__(self, parent: QObject):
        super().__init__(parent)
        # 当前连接的串口对象
        self.serial = None
        # 表示当前线程是否是正在连接中
        self.connecting = False
        # 表示当前串口是否处于已连接状态
        self.connected = False

        # 当前串口读取线程对象
        self.serialReadThread = None

        self.timer = QTimer()
        self.timer.setInterval(1000)
        self.timer.timeout.connect(self.onTimer_timeout)
        self.timer.start()
    
    # 列举所有的串口
    def list_ports(self):
        return [(p.description, p.device) for p in list_ports.comports()]
    
    # 判定当前串口是否处于连接状态
    def is_open(self) -> bool:
        return True if self.serial and self.serial.is_open else False
    
    # 定时器处理函数
    def onTimer_timeout(self):
        if not self.serial:
            return
        # print("[%s]timer:connected=%d, open=%d" % (now(), self.connected, self.serial.is_open))
        connected = self.serial.is_open
        if connected != self.connected:
            # 状态发生变化，则立即更新状态，然后发出信号
            self.connected = connected
            self.statusChanged.emit(connected)
    
    # 断开连接
    def disconnect(self):
        if self.is_open():
            self.serial.close()
    
    # 连接成功后的处理函数
    def __connected__(self, f:Future):
        self.connecting = False
        e = f.exception()
        if e is not None:
            # 连接发生了异常，立即报告该异常
            self.error.emit(e)
            self.statusChanged.emit(self.is_open())
            return
        s = f.result()
        
        if isinstance(s, Serial):
            # 成功连接
            self.serial = s
            self.__start_receive__()
        else:
            self.error.emit(Exception("没有成功连接"))
            self.statusChanged.emit(self.seri.is_open())
    
    # 连接成功后的处理函数
    def __read__(self, s:Serial):
        while s:
            if s.inWaiting() > 0:
                bs = s.read(s.inWaiting())	#这里如果没有接收到数据就会阻塞在这里
                self.received.emit(bs)
    
    # 执行数据接收（开启线程）
    def __start_receive__(self):
        s = self.serial
        if not s:
            return
        # 开线程进行数据接收
        if self.serialReadThread is None:
            self.serialReadThread = Thread(target=self.__read__, name='SerialReadThread', kwargs= {'s' : s})
            self.serialReadThread.daemon = True
            self.serialReadThread.start()
        else:
            if not self.serialReadThread.is_alive():
                # 如果线程是不存活的, 需要启用线程
                self.serialReadThread.start()
    
    # port      = 'COM1'	读或者写端口
    # baudrate  = 115200	波特率
    # bytesize  = 8         字节大小
    # parity    = 'N'	    校验位
    # stopbits  = 1	        停止位
    # timeout   = None	    超时设置
    # xonxoff   = False	    软件流控
    # rtscts    = False	    硬件流控
    # dsrdtr    = False	    硬件流控
    def connect(self, port:str, baudrate:int):
        if self.connecting:
            return
        self.connecting = True
        executor = ThreadPoolExecutor(max_workers=2)
        future = executor.submit(__connect__, port=port, baudrate=baudrate)
        future.add_done_callback(self.__connected__)
    
    def send(self, bs:bytes):
        if not isinstance(bs, bytes):
            return
        self.serial.write(bs)
        self.serial.flush()
